package com.tencent.compose.sample.mainpage.sectionItem


import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.math.floor
import kotlin.math.ln
import kotlin.math.pow
import kotlin.math.round
import kotlin.random.Random
import kotlin.time.TimeSource

data class Sample(val tMs: Long, val all: Int, val normal: Int, val video: Int)

@Composable
internal fun The300Threads() {
    val scope = rememberCoroutineScope()
    var running by remember { mutableStateOf(false) }
    var progress by remember { mutableStateOf(0) }
    val total = 300

    val logs = remember { mutableStateListOf<String>() }
    val logClock = remember { TimeSource.Monotonic }

    val samples = remember { mutableStateListOf<Sample>() }
    var runStart by remember { mutableStateOf(TimeSource.Monotonic.markNow()) }

    fun pushLog(line: String) {
        val ts = logClock.markNow().elapsedNow().inWholeMilliseconds
        val msg = "[$ts] $line"
        println(msg)
        logs += msg
        if (logs.size > 2000) logs.removeFirst()
    }

    fun pushSample(all: Int, normal: Int, video: Int) {
        val t = runStart.elapsedNow().inWholeMilliseconds
        samples += Sample(t, all, normal, video)
        if (samples.size > 4000) samples.removeFirst()
    }

    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp)
        ) {
            // 顶部操作区
            Row(verticalAlignment = Alignment.CenterVertically) {
                Button(
                    enabled = !running,
                    onClick = {
                        running = true
                        progress = 0
                        logs.clear()
                        samples.clear()
                        runStart = TimeSource.Monotonic.markNow()

                        scope.launch {
                            val totalTimer = TimeSource.Monotonic.markNow()
                            pushLog("LAUNCH mixed tasks: 299 normal + 1 video")
                            val specialIndex = total - 1

                            runNCoroutinesMixedVerbose(
                                total = total,
                                specialIndex = specialIndex,
                                onProgress = { v ->
                                    withContext(Dispatchers.Main) { progress = v }
                                },
                                onLog = { line ->
                                    withContext(Dispatchers.Main) { pushLog(line) }
                                },
                                onActiveChange = { all, normal, video ->
                                    withContext(Dispatchers.Main) { pushSample(all, normal, video) }
                                },
                                doNormalWork = { simulateNormalWork() },
                                doVideoWork = { simulateVideoWork() }
                            )

                            val cost = totalTimer.elapsedNow().inWholeMilliseconds
                            pushLog("ALL DONE in ${cost}ms")
                            running = false
                        }
                    }
                ) { Text(if (running) "运行中…" else "Run 300 Mixed Tasks") }

                Spacer(Modifier.width(12.dp))
                Button(enabled = !running, onClick = {
                    logs.clear(); samples.clear()
                }) { Text("清空") }
            }

            Spacer(Modifier.height(8.dp))
            Text("进度：$progress / $total", fontWeight = FontWeight.Medium)

            Spacer(Modifier.height(12.dp))

            ConcurrencyChart(
                samples = samples.toList(),
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
                    .border(1.dp, Color(0x22000000))
                    .background(Color(0xFFF9FAFB))
                    .padding(8.dp)
            )

            Spacer(Modifier.height(12.dp))

            val scroll = rememberScrollState()
            LaunchedEffect(logs.size) { scroll.animateScrollTo(scroll.maxValue) }
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .weight(1f)
                    .verticalScroll(scroll)
                    .padding(8.dp)
                    .background(Color(0xFFF3F4F6))
            ) {
                Text(logs.joinToString("\n"))
            }
        }
    }
}

suspend fun runNCoroutinesMixedVerbose(
    total: Int,
    specialIndex: Int,
    onProgress: suspend (Int) -> Unit,
    onLog: suspend (String) -> Unit,
    onActiveChange: suspend (all: Int, normal: Int, video: Int) -> Unit,
    doNormalWork: suspend () -> Unit,
    doVideoWork: suspend () -> Unit
) = coroutineScope {
    val progressMutex = Mutex()
    var counter = 0

    val activeMutex = Mutex()
    var activeAll = 0
    var activeNormal = 0
    var activeVideo = 0

    suspend fun reportActive() {
        val (a, n, v) = activeMutex.withLock { Triple(activeAll, activeNormal, activeVideo) }
        onActiveChange(a, n, v)
    }

    suspend fun inc(isVideo: Boolean) {
        activeMutex.withLock {
            activeAll++
            if (isVideo) activeVideo++ else activeNormal++
        }
        reportActive()
    }

    suspend fun dec(isVideo: Boolean) {
        activeMutex.withLock {
            activeAll--
            if (isVideo) activeVideo-- else activeNormal--
        }
        reportActive()
    }

    repeat(total) { id ->
        val isVideo = (id == specialIndex)
        val name = if (isVideo) "video-$id" else "normal-$id"
        val dispatcher = if (isVideo) Dispatchers.IO else Dispatchers.Default

        launch(dispatcher + CoroutineName(name)) {
            val coName = coroutineContext[CoroutineName]?.name ?: name
            onLog("LAUNCH $coName")

            inc(isVideo)

            val t = TimeSource.Monotonic.markNow()
            onLog("START  $coName")

            try {
                if (isVideo) doVideoWork() else doNormalWork()

                val elapsed = t.elapsedNow().inWholeMilliseconds
                val v = progressMutex.withLock { ++counter }
                onLog("END    $coName elapsed=${elapsed}ms progress=$v/$total")
                onProgress(v)

            } catch (e: CancellationException) {
                onLog("CANCEL $coName")
                throw e
            } catch (e: Throwable) {
                val v = progressMutex.withLock { ++counter }
                onLog("ERROR  $coName -> ${e::class.simpleName}: ${e.message} progress=$v/$total")
                onProgress(v)
            } finally {
                dec(isVideo)
            }
        }
    }
}

private suspend fun simulateNormalWork() {
    delay(Random.nextLong(1000, 5000))
}
private suspend fun simulateVideoWork() {
    delay(1500)
    delay(2000)
    repeat(5) { delay(1200) }
}

@Composable
private fun ConcurrencyChart(
    samples: List<Sample>,
    modifier: Modifier = Modifier
) {
    if (samples.isEmpty()) {
        Box(modifier, contentAlignment = Alignment.Center) {
            androidx.compose.material.Text("等待任务开始…")
        }
        return
    }

    val maxAll = (samples.maxOfOrNull { it.all } ?: 0).coerceAtLeast(1)

    val t0 = samples.first().tMs
    val timeMax = (samples.last().tMs - t0).coerceAtLeast(1L)

    val xTicks = buildNiceTimeTicks(0.0, timeMax.toDouble(), targetTickCount = 6)

    val textMeasurer = rememberTextMeasurer()
    val tickTextStyle = TextStyle(fontSize = 10.sp, color = Color(0xFF374151)) // gray-700

    Canvas(modifier = modifier) {
        val w = size.width
        val h = size.height

        val padL = 52f
        val padR = 12f
        val padT = 12f
        val padB = 30f

        val chartW = w - padL - padR
        val chartH = h - padT - padB

        fun xAt(tRelMs: Long): Float {
            val ratio = tRelMs / timeMax.toFloat()
            return padL + chartW * ratio.coerceIn(0f, 1f)
        }
        fun yAt(v: Int): Float {
            val ratio = v / maxAll.toFloat()
            return padT + chartH * (1f - ratio)
        }

        val ySteps = 5
        for (i in 0..ySteps) {
            val v = maxAll * i / ySteps
            val y = yAt(v)
            drawLine(
                start = Offset(padL, y),
                end = Offset(padL + chartW, y),
                color = Color(0x22000000),
                strokeWidth = 1f
            )

            val layout = textMeasurer.measure(AnnotatedString(v.toString()), style = tickTextStyle)
            drawText(
                textLayoutResult = layout,
                topLeft = Offset(8f, y - layout.size.height / 2f)
            )
        }
        for (tick in xTicks) {
            val x = xAt(tick.toLong())
            drawLine(
                start = Offset(x, padT),
                end = Offset(x, padT + chartH),
                color = Color(0x22000000),
                strokeWidth = 1f
            )

            val label = formatDurationShort(tick.toLong()) // 如 0ms / 200ms / 1.2s
            val layout = textMeasurer.measure(AnnotatedString(label), style = tickTextStyle)
            drawText(
                textLayoutResult = layout,
                topLeft = Offset(x - layout.size.width / 2f, h - layout.size.height - 4f)
            )
        }

        drawLine(
            start = Offset(padL, padT),
            end = Offset(padL, padT + chartH),
            color = Color(0x99000000),
            strokeWidth = 2f
        )
        drawLine(
            start = Offset(padL, padT + chartH),
            end = Offset(padL + chartW, padT + chartH),
            color = Color(0x99000000),
            strokeWidth = 2f
        )

        fun drawSeries(selector: (Sample) -> Int, color: Color, stroke: Float) {
            val path = Path()
            val first = samples.first()
            path.moveTo(xAt(first.tMs - t0), yAt(selector(first)))
            for (i in 1 until samples.size) {
                val s = samples[i]
                path.lineTo(xAt(s.tMs - t0), yAt(selector(s)))
            }
            drawPath(path, color = color, style = Stroke(width = stroke))
        }

        drawSeries({ it.all },    Color(0xFF2563EB), 3f)   // blue-600
        drawSeries({ it.normal }, Color(0xFF16A34A), 2f)   // green-600
        drawSeries({ it.video },  Color(0xFFDC2626), 2.5f) // red-600
    }
}

private fun buildNiceTimeTicks(
    min: Double,
    max: Double,
    targetTickCount: Int = 6
): List<Double> {
    val range = (max - min).coerceAtLeast(1.0)
    val rawStep = range / targetTickCount
    val mag = 10.0.pow(floor(ln(rawStep) / ln(10.0)))
    val candidates = doubleArrayOf(1.0, 2.0, 5.0, 10.0)    // 1/2/5 系列
    val step = candidates.minByOrNull { kotlin.math.abs(rawStep - it * mag) }!! * mag

    val start = floor(min / step) * step
    val end = round(max / step) * step

    val ticks = ArrayList<Double>()
    var v = start
    while (v <= end + step * 0.5) {
        ticks += v
        v += step
    }
    return ticks
}

private fun formatDurationShort(ms: Long): String {
    return if (ms < 1000L) {
        "${ms}ms"
    } else {
        val s = ms / 1000.0
        val rounded = round(s * 10) / 10.0
        "${rounded}s"
    }
}